<!-- 
  ChatTestDialog.vue
  智能体测试对话组件
  封装了完整的对话测试功能，包括WebSocket连接、消息处理和UI展示
-->
<template>
  <div class="chat-test-dialog">
    <div class="dialog-header">
      <div class="connection-status">
        <el-tag :type="wsConnected ? 'success' : 'danger'" size="small">
          {{ wsConnected ? 'AI服务连接成功' : 'AI服务未连接' }}
        </el-tag>
        <el-button
          v-if="!wsConnected"
          type="warning"
          size="small"
          @click="initWebSocket"
          :loading="connecting">
          重新连接
        </el-button>
      </div>
      <div class="dialog-controls">
        <el-button
          type="primary"
          size="small"
          @click="initTestDialog">
          重新开始
        </el-button>
        <el-button
          v-if="testing"
          type="danger"
          size="small"
          @click="stopTest">
          终止测试
        </el-button>
        <el-switch
          v-model="enableContinuousDialog"
          active-text="连续对话"
          inactive-text="单次对话"
          class="dialog-switch"
        />
      </div>
    </div>

    <div class="messages-container">
      <div class="messages" ref="messagesRef">
        <div v-for="(msg, index) in testMessages"
             :key="index"
             :class="['message', msg.role]">
          <div v-if="msg.role === 'user'" class="content user-content">
            {{ msg.content }}
          </div>
          <div v-else-if="msg.role === 'reasoning'" class="content-wrapper">
            <div class="content reasoning-content">
              {{ msg.content }}
            </div>
            <el-button
              type="info"
              size="small"
              circle
              class="speak-button"
              @click="speakMessage(msg.content)"
              title="朗读思考过程"
            >
              <i class="el-icon-microphone">🔊</i>
            </el-button>
          </div>
          <div v-else class="content-wrapper">
            <div class="content markdown-body" v-html="renderMarkdown(msg.content)"></div>
            <el-button
              v-if="msg.role === 'assistant'"
              type="primary"
              size="small"
              circle
              class="speak-button"
              @click="speakMessage(msg.content)"
              title="朗读此消息"
            >
              <i class="el-icon-microphone">🔊</i>
            </el-button>
          </div>
        </div>
      </div>
    </div>

    <div class="input-area">
      <el-input
        v-model="testInput"
        type="textarea"
        :rows="3"
        placeholder="输入测试消息，Ctrl + Enter 发送"
        @keyup.enter.ctrl="handleTestSend"
      />
      <el-button
        type="primary"
        :loading="testing"
        @click="handleTestSend">
        发送
      </el-button>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick, onMounted, onBeforeUnmount, watch } from 'vue'
import { ElMessage } from 'element-plus'
import MarkdownIt from 'markdown-it'

const props = defineProps({
  agentId: {
    type: [String, Number],
    required: true
  },
  config: {
    type: Object,
    required: true
  }
})

// 测试相关的状态
const testing = ref(false)
const testInput = ref('')
const testMessages = ref([])
const messagesRef = ref(null)

// WebSocket 相关状态
const wsConnected = ref(false)
const connecting = ref(false)
let websocket = null

// 语音朗读相关状态
const enableVoice = ref(false)
const speechSynthesis = window.speechSynthesis
let speechUtterance = null

// 连续对话开关
const enableContinuousDialog = ref(true)

// 创建 Markdown 渲染器
const md = new MarkdownIt({
  html: true,
  breaks: true,
  linkify: true
})

// 渲染 Markdown 内容
const renderMarkdown = (content) => {
  return md.render(content || '')
}

// 初始化测试对话
const initTestDialog = () => {
  testMessages.value = [{
    role: 'system',
    content: '测试会话已开始。我是一个配置中的智能体，请开始对话。' +
      (enableContinuousDialog.value ? '我会记住我们的对话历史。' : '')
  }]
  testInput.value = ''
}

// 初始化 WebSocket 连接
const initWebSocket = () => {
  // 设置连接状态
  connecting.value = true

  // 关闭之前的连接
  if (websocket) {
    websocket.close()
  }

  // 创建新的 WebSocket 连接
  websocket = new WebSocket('wss://adminstore.saas.nbsaas.com/websocket')

  // 连接建立时的回调
  websocket.onopen = () => {
    wsConnected.value = true
    connecting.value = false
    ElMessage.success('AI服务连接成功')
    console.log('WebSocket 连接已建立')
  }

  // 接收消息的回调
  websocket.onmessage = (event) => {
    try {
      const data = JSON.parse(event.data)
      // 处理消息
      if (data.type === 'reply') {
        if (data.messageType === 'assistant' || data.messageType === 'content') {
          if(data.content===""){
            return;
          }
          // 这是助手的回复内容
          if (isLastMessageAssistant()) {
            // 如果最后一条是助手消息，追加内容
            const lastMsg = testMessages.value[testMessages.value.length - 1]
            lastMsg.content += data.content || ''
          } else {
            // 否则创建新的助手消息
            testMessages.value.push({
              role: 'assistant',
              content: data.content || ''
            })
          }
          testing.value = true // 标记为正在接收回复
        } else if (data.messageType === 'reasoning') {
          testing.value = true
          // 处理推理消息 - 作为单独的消息类型
          if (isLastMessageReasoning()) {
            // 如果最后一条是推理消息，追加内容
            const lastMsg = testMessages.value[testMessages.value.length - 1]
            lastMsg.content += data.content || ''
          } else {
            console.log('推理消息:', data.content)
            // 否则创建新的推理消息
            testMessages.value.push({
              role: 'reasoning',
              content: data.content || ''
            })
          }
        } else if (data.messageType === 'over') {
          // 回复完成
          testing.value = false
        }
      } else if (data.type === 'error') {
        // 处理错误
        ElMessage.error(data.message || '发生错误')
        testing.value = false
      }

      // 滚动到底部
      nextTick(() => {
        if (messagesRef.value) {
          messagesRef.value.scrollTop = messagesRef.value.scrollHeight
        }
      })
    } catch (error) {
      console.error('解析 WebSocket 消息失败:', error)
    }
  }

  // 连接关闭的回调
  websocket.onclose = () => {
    wsConnected.value = false
    connecting.value = false
    console.log('WebSocket 连接已关闭')
  }

  // 连接错误的回调
  websocket.onerror = (error) => {
    wsConnected.value = false
    connecting.value = false
    console.error('WebSocket 连接错误:', error)
    ElMessage.error('AI服务连接失败')
  }
}

// 辅助函数：检查最后一条消息是否是助手消息
const isLastMessageAssistant = () => {
  return testMessages.value.length > 0 &&
         testMessages.value[testMessages.value.length - 1].role === 'assistant'
}

// 辅助函数：检查最后一条消息是否是推理消息
const isLastMessageReasoning = () => {
  return testMessages.value.length > 0 &&
         testMessages.value[testMessages.value.length - 1].role === 'reasoning'
}

// 修改语音朗读函数，添加朗读特定消息的功能
const speakMessage = (content) => {
  if (!content || !speechSynthesis) return

  // 取消之前的语音
  if (speechUtterance) {
    speechSynthesis.cancel()
  }

  // 从Markdown提取纯文本
  const plainText = extractTextFromMarkdown(content)

  // 创建新的语音实例
  speechUtterance = new SpeechSynthesisUtterance(plainText)

  // 设置语音属性
  speechUtterance.lang = 'zh-CN' // 设置语言为中文
  speechUtterance.rate = 1.0 // 语速
  speechUtterance.pitch = 1.0 // 音调

  // 开始朗读
  speechSynthesis.speak(speechUtterance)
}

// 从Markdown文本中提取纯文本
const extractTextFromMarkdown = (markdown) => {
  // 简单替换一些常见的Markdown语法
  return markdown
    .replace(/```[\s\S]*?```/g, '') // 移除代码块
    .replace(/`([^`]+)`/g, '$1') // 移除内联代码
    .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // 替换链接为链接文本
    .replace(/!\[[^\]]*\]\([^)]+\)/g, '') // 移除图片
    .replace(/#{1,6}\s+([^\n]+)/g, '$1') // 替换标题为纯文本
    .replace(/\*\*([^*]+)\*\*/g, '$1') // 移除粗体
    .replace(/\*([^*]+)\*/g, '$1') // 移除斜体
    .replace(/- /g, '') // 移除列表标记
    .replace(/\d+\. /g, '') // 移除有序列表标记
    .trim()
}

// 发送测试消息
const handleTestSend = async () => {
  if (!testInput.value.trim() || testing.value) return

  const userMessage = testInput.value.trim()
  testMessages.value.push({
    role: 'user',
    content: userMessage
  })
  testInput.value = ''

  // 如果 WebSocket 已连接，则通过 WebSocket 发送消息
  if (wsConnected.value && websocket && websocket.readyState === WebSocket.OPEN) {
    // 准备历史消息数组
    let messages = [];

    // 添加系统提示（如果有）
    if (props.config.systemPrompt) {
      messages.push({
        role: 'system',
        content: props.config.systemPrompt
      });
    }

    // 根据连续对话开关决定是否添加历史消息
    if (enableContinuousDialog.value) {
      // 添加历史对话消息（排除系统消息和推理消息）
      testMessages.value.forEach(msg => {
        if (msg.role === 'user' || msg.role === 'assistant') {
          messages.push({
            role: msg.role,
            content: msg.content
          });
        }
      });
    } else {
      // 只添加当前用户消息
      messages.push({
        role: 'user',
        content: userMessage
      });
    }

    // 发送批量消息
    websocket.send(JSON.stringify({
      type: 'batchV3',
      model:props.config.model,
      message: JSON.stringify(messages),
      agentId: props.agentId,
      config: props.config
    }));

    // 滚动到底部
    await nextTick()
    if (messagesRef.value) {
      messagesRef.value.scrollTop = messagesRef.value.scrollHeight
    }
    return
  }

  // 如果 WebSocket 未连接，使用 HTTP 请求
  testing.value = true // 这里设置为 true，因为 HTTP 请求不是流式的
  try {
    // 准备历史消息数组
    let messages = [];

    // 根据连续对话开关决定是否添加历史消息
    if (enableContinuousDialog.value) {
      // 添加历史对话消息（排除系统消息和推理消息）
      testMessages.value.forEach(msg => {
        if (msg.role === 'user' || msg.role === 'assistant') {
          messages.push({
            role: msg.role,
            content: msg.content
          });
        }
      });
    } else {
      // 只添加当前用户消息
      messages.push({
        role: 'user',
        content: userMessage
      });
    }

    const response = await fetch(`/api/agents/${props.agentId}/test`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messages: messages,
        config: props.config
      })
    })

    const data = await response.json()
    if (data.success) {
      testMessages.value.push({
        role: 'assistant',
        content: data.response
      })

      // 滚动到底部
      await nextTick()
      if (messagesRef.value) {
        messagesRef.value.scrollTop = messagesRef.value.scrollHeight
      }
    } else {
      throw new Error(data.message)
    }
  } catch (error) {
    console.error('测试对话失败:', error)
    ElMessage.error('测试对话失败')
  } finally {
    testing.value = false
  }
}

// 停止语音朗读
const stopSpeaking = () => {
  if (speechSynthesis) {
    speechSynthesis.cancel()
  }
}

// 添加终止测试功能
const stopTest = () => {
  // 停止语音朗读
  stopSpeaking()

  // 如果WebSocket连接存在，发送终止消息
  if (websocket && websocket.readyState === WebSocket.OPEN) {
    try {
      websocket.send(JSON.stringify({
        type: 'stop',
        agentId: props.agentId
      }));
    } catch (error) {
      console.error('发送终止消息失败:', error);
    }
  }

  // 重置测试状态
  testing.value = false

  // 添加系统消息，表示测试已终止
  testMessages.value.push({
    role: 'system',
    content: '测试已终止'
  })

  // 滚动到底部
  nextTick(() => {
    if (messagesRef.value) {
      messagesRef.value.scrollTop = messagesRef.value.scrollHeight
    }
  })
}

// 监听语音开关变化
watch(enableVoice, (newValue) => {
  if (!newValue) {
    // 如果关闭语音，停止当前朗读
    stopSpeaking()
  }
})

// 监听连续对话开关变化
watch(enableContinuousDialog, (newValue) => {
  // 当开关状态变化时，更新系统消息
  const systemMsg = testMessages.value.find(msg => msg.role === 'system');
  if (systemMsg) {
    systemMsg.content = '测试会话已开始。我是一个配置中的智能体，请开始对话。' +
      (newValue ? '我会记住我们的对话历史。' : '');
  }
});

// 组件挂载时初始化 WebSocket 和测试对话
onMounted(() => {
  initWebSocket()
  initTestDialog()
})

// 组件卸载前关闭 WebSocket 连接和停止语音
onBeforeUnmount(() => {
  if (websocket) {
    websocket.close()
    websocket = null
  }

  // 停止语音朗读
  stopSpeaking()
})
</script>

<style scoped>
.chat-test-dialog {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0;
}

.dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid #ebeef5;
  background-color: #f8f9fa;
  border-radius: 4px 4px 0 0;
}

.connection-status, .dialog-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}

.messages-container {
  flex: 1;
  height: 100%;
  overflow: hidden;
  position: relative;
}

.messages {
  height: 100%;
  overflow-y: auto;
  padding: 16px;
  background-color: #fafafa;
}

.message {
  margin-bottom: 16px;
  animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.message .content {
  padding: 12px;
  border-radius: 8px;
  max-width: 85%;
  word-break: break-word;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.message.system .content {
  background-color: #f4f4f5;
  margin: 0 auto;
  text-align: center;
  font-size: 13px;
  color: #909399;
  max-width: 100%;
  box-shadow: none;
}

.message.user .content {
  background-color: #ecf5ff;
  margin-left: auto;
  border: 1px solid #d9ecff;
  color: #409EFF;
}

.message.assistant .content {
  background-color: #f0f9eb;
  margin-right: auto;
  border: 1px solid #e1f3d8;
  color: #67c23a;
}

.input-area {
  display: flex;
  gap: 10px;
  padding: 16px;
  background-color: #fff;
  border-top: 1px solid #ebeef5;
  border-radius: 0 0 4px 4px;
}

.input-area .el-input {
  flex: 1;
}

.reasoning-content {
  background-color: #f8f9fa;
  color: #606266;
  font-style: italic;
  white-space: pre-wrap;
  border-left: 3px solid #dcdfe6;
}

.message.reasoning .content {
  background-color: #f8f9fa;
  color: #606266;
  font-style: italic;
  margin-right: auto;
  border-left: 3px solid #dcdfe6;
}

:deep(.markdown-body) {
  font-size: 14px;
  line-height: 1.6;
  color: #2c3e50;
}

:deep(.markdown-body p) {
  margin: 8px 0;
}

:deep(.markdown-body pre) {
  background-color: #f6f8fa;
  padding: 16px;
  border-radius: 6px;
  margin: 12px 0;
  border: 1px solid #ebeef5;
  overflow-x: auto;
}

:deep(.markdown-body code) {
  font-family: 'Courier New', Consolas, monospace;
  font-size: 13px;
  padding: 2px 6px;
  background-color: #f6f8fa;
  border-radius: 4px;
}

:deep(.markdown-body pre code) {
  padding: 0;
  background-color: transparent;
}

:deep(.markdown-body ul), :deep(.markdown-body ol) {
  padding-left: 24px;
  margin: 8px 0;
}

:deep(.markdown-body blockquote) {
  margin: 16px 0;
  padding: 0 16px;
  color: #666;
  border-left: 4px solid #dcdfe6;
}

.dialog-switch {
  margin-left: 10px;
}

.content-wrapper {
  position: relative;
  display: flex;
  align-items: flex-start;
  max-width: 85%;
}

.message.assistant .content-wrapper {
  margin-right: auto;
}

.speak-button {
  margin-left: 8px;
  flex-shrink: 0;
  align-self: flex-start;
  margin-top: 10px;
}
</style> 